package com.jeasonzhao.model;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.sql.DataSource;

import com.jeasonzhao.commons.db.SqlParameter;
import com.jeasonzhao.commons.db.Sqlca;
import com.jeasonzhao.commons.parser.lex.SqlTokenizer;
import com.jeasonzhao.commons.xml.XMLNode;

public class ActiveRecord extends Sqlca
{
    public ActiveRecord(Connection newConnection)
    {
        super(newConnection);
    }

    public ActiveRecord(DataSource ds)
        throws SQLException
    {
        super(ds);
    }

    public ActiveRecord(DataSource ds,String strUser,String strPwd)
        throws SQLException
    {
        super(ds,strUser,strPwd);
    }

    @SuppressWarnings("unchecked")
    public <T> T nextObject(Class<?> clsInit)
        throws ModelException,SQLException
    {
        if(this.next())
        {
            return(T) ModelHelper.getInstance().fromDatabase(clsInit,this.getResultSet());
        }
        else
        {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public <T> T initObject(Class<?> clsInit)
        throws ModelException,SQLException
    {
        return(T) ModelHelper.getInstance().fromDatabase(clsInit,this.getResultSet());
    }

    @SuppressWarnings("unchecked")
    public <T> T initFromObject(Object obj)
        throws ModelException
    {
        return(T) ModelHelper.getInstance().initFromDatabase(this,obj);
    }

    public int update(Object obj)
        throws ModelException
    {
        if(null == obj)
        {
            return 0;
        }
        if(Collection.class.isAssignableFrom(obj.getClass()))
        {
            Collection<?> c = (Collection<?>) obj;
            int nx = 0;
            for(java.util.Iterator<?> it = c.iterator();it.hasNext();)
            {
                nx += ModelHelper.getInstance().updateDatabase(this,it.next());
            }
            return nx;
        }
        else
        {
            return ModelHelper.getInstance().updateDatabase(this,obj);
        }
    }

    public int save(Object obj)
        throws ModelException
    {
        if(null == obj)
        {
            return 0;
        }
        if(Collection.class.isAssignableFrom(obj.getClass()))
        {
            Collection<?> c = (Collection<?>) obj;
            int nx = 0;
            for(java.util.Iterator<?> it = c.iterator();it.hasNext();)
            {
                Object i = it.next();
                int n = update(i);
                if(n < 1)
                {
                    n = insertInto(i,null);
                }
                nx += n;
            }
            return nx;
        }
        else
        {
            int n = update(obj);
            if(n < 1)
            {
                n = insertInto(obj,null);
            }
            return n;
        }
    }

    public int insertInto(Object obj)
        throws ModelException
    {
        return insertInto(obj,null);
    }

    public int insertInto(Object obj,String strTableName)
        throws ModelException
    {
        if(null == obj)
        {
            return 0;
        }
        if(Collection.class.isAssignableFrom(obj.getClass()))
        {
            Collection<?> c = (Collection<?>) obj;
            int nx = 0;
            for(java.util.Iterator<?> it = c.iterator();it.hasNext();)
            {
                nx += ModelHelper.getInstance().insertIntoDatabase(this,it.next(),strTableName);
            }
            return nx;
        }
        else
        {
            return ModelHelper.getInstance().insertIntoDatabase(this,obj,strTableName);
        }
    }

    public int delete(Object obj)
        throws ModelException
    {
        if(null == obj)
        {
            return 0;
        }
        if(Collection.class.isAssignableFrom(obj.getClass()))
        {
            Collection<?> c = (Collection<?>) obj;
            int nx = 0;
            for(java.util.Iterator<?> it = c.iterator();it.hasNext();)
            {
                nx += ModelHelper.getInstance().deleteFromDatabase(this,it.next());
            }
            return nx;
        }
        else
        {
            return ModelHelper.getInstance().deleteFromDatabase(this,obj);
        }
    }

    private String prepareSQL(Class<?> clsInit,String strSql)
        throws ModelException,SQLException
    {
        if(clsInit == null || strSql == null)
        {
            return strSql;
        }
        MappedInformation info = ModelHelper.getInstance().getMappedInformation(clsInit,(XMLNode)null);
        if(null == info.getMappedItems() || info.getMappedItems().size() < 1)
        {
            throw new ModelException("The class " + clsInit.getName() + " has no field mapped by annotation Model");
        }
        if(strSql.trim().length() > 0)
        {
            strSql = SqlTokenizer.cleanSQL(strSql.trim()).trim();
            if(strSql.toLowerCase().startsWith("select"))
            {
                return strSql;
            }
            else if(strSql.toLowerCase().startsWith("order")
                    || strSql.toLowerCase().startsWith("group"))
            {
                strSql = "SELECT * FROM " + info.getName() + " " + strSql;
            }
            else if(strSql.toLowerCase().startsWith("where"))
            {
                strSql = "SELECT * FROM " + info.getName() + " " + strSql;
            }
            else
            {
                strSql = "SELECT * FROM " + info.getName() + " WHERE " + strSql;
            }
            return strSql;
        }
        else
        {
            strSql = "SELECT * FROM " + info.getName();
        }
        return strSql;
    }

    @SuppressWarnings("unchecked")
    private <T> List<T> fetchItems(Class<?> cls)
        throws ModelException,SQLException
    {
        List<T> ret = new ArrayList<T>();
        while(this.next())
        {
            T object = (T) ModelHelper.getInstance().fromDatabase(cls,this);
            ret.add(object);
        }
        return ret;
    }

    public <T> List<T> query(Class<?> cls)
        throws ModelException,SQLException
    {
        execute(this.prepareSQL(cls,null));
        return fetchItems(cls);
    }

    public <T> List<T> query(Class<?> cls,String strSql)
        throws ModelException,SQLException
    {
        execute(this.prepareSQL(cls,strSql));
        return fetchItems(cls);
    }

    public <T> List<T> query(Class<?> cls,String strSql,Iterable<SqlParameter> allParameters)
        throws ModelException,SQLException
    {
        execute(this.prepareSQL(cls,strSql),allParameters);
        return fetchItems(cls);
    }

    public <T> List<T> query(Class<?> cls,String strSql,SqlParameter ...allParameters)
        throws ModelException,SQLException
    {
        execute(this.prepareSQL(cls,strSql),allParameters);
        return fetchItems(cls);
    }

    public <T> List<T> query(Class<?> cls,String strSql,Object ...allParameters)
        throws ModelException,SQLException
    {
        execute(this.prepareSQL(cls,strSql),allParameters);
        return fetchItems(cls);
    }

    public int insertBeanInto(Object obj,String strTableName)
        throws SQLException
    {
        if(null == obj)
        {
            return 0;
        }
        if(ModelHelper.getInstance().isRegistered(obj.getClass()))
        {
            try
            {
                return this.insertInto(obj,strTableName);
            }
            catch(ModelException ex)
            {
                throw new SQLException("Model Error: " + ex.getMessage());
            }
        }
        else
        {
            return super.insertBeanInto(obj,strTableName);
        }
    }

    public <T> List<T> queryBeans(Class<?> cls,String strSql,Object ...allParameters)
        throws SQLException
    {
        if(null == cls)
        {
            return null;
        }
        if(ModelHelper.getInstance().isRegistered(cls))
        {
            try
            {
                return this.query(cls,strSql,allParameters);
            }
            catch(ModelException ex)
            {
                throw new SQLException("Model Error: " + ex.getMessage());
            }
        }
        else
        {
            return super.queryBeans(cls,strSql,allParameters);
        }
    }

    public <T> List<T> queryBeans(Class<?> cls)
        throws SQLException
    {
        if(null == cls)
        {
            return null;
        }
        if(ModelHelper.getInstance().isRegistered(cls))
        {
            try
            {
                return this.query(cls);
            }
            catch(ModelException ex)
            {
                throw new SQLException("Model Error: " + ex.getMessage());
            }
        }
        else
        {
            return super.queryBeans(cls);
        }
    }

    public Object fillBean(Object obj)
        throws SQLException,IllegalAccessException,IllegalArgumentException,InvocationTargetException,NoSuchMethodException
    {
        if(null == obj)
        {
            return super.fillBean((Object)null);
        }
        else if(ModelHelper.getInstance().isRegistered(obj.getClass()))
        {
            try
            {
                return ModelHelper.getInstance().fromDatabase(obj.getClass(),this);
            }
            catch(ModelException ex)
            {
                throw new SQLException("Model Error: " + ex.getMessage());
            }
        }
        else
        {
            return super.fillBean(obj);
        }
    }
}
